Wavefront algorithm

Wavefront 演算法是BFS(廣度優先搜索)演算法的其中一種,常見於2D網格地圖中用以找尋路徑。在wavefront算法中,隊列以源節點初始化,算法通過按照節點距離源節點的特定順序將相鄰節點添加到隊列中來進行進展。這創建了一個從源節點向外擴展的“wavefront”。

1. 準備網格2D地圖

image-20230515131823352

這裡介紹多一個可以快速開始2D list的方法。在以前的做法中,我們會用到兩個for loop,而在第一個for loop中會加入一個暫存的list,例子如下:

但其實可以用一個較簡單的方法。Python提供一個先寫結果,再寫condition的寫法,for loop和if都有這選項,不用每次為寫一個簡單的for和if都要分兩行來寫,另一個好處是可以很容易地做到巢狀的for和if。

2. 為每個格加入鄰居和加入牆

image-20230515132234289

首先為Spot class加入self.wall變數,用來設定當前的格是否牆。另外再加入self.neighbors = []的空list,用來裝起每個格的鄰居。

加入兩個函數,隨機抽出01之間的任意值,如果是少於0.25的話,就將self.wall設定成True,否則就是False

之後就將為每一個spot加入鄰居,如果鄰居不是牆,而且又不是邊緣的話,就將旁邊的spot加入做鄰居。這裡的做法跟第7章十分相似,如果忘記的同學可以參考第7章的7. Snake

最後將顯示顏色變成一個輸入的變數。

 

setup()中,加入startend的位置,分別為於左上角和右下角。如果這個spot不是起點和終點的話,就隨機加入牆,之後就加入鄰居。

最後獨立將start和end顯示成不同顏色。

3. 為每個格打上分數

wavefront1

這一步我們要先準備前置工作。開3個新的變數,pathIsFound用來紀錄路徑是否已經找到。path=[]用來裝起路徑的spot,queue=[]用來裝起待處理的格。

 

在spot class中,加入self.visited = False用來紀錄這格是否已經計算過,self.score = 0用來紀錄這一格的分數。

show()中,將分數顯示在畫面上。

最後加入兩個函數,用來設定已訪問過和回傳是否訪問過。

 

setup()中,先將start設定成已訪問過,並放入queue候選列。

draw()中,將queue候選列中的第一個用pop釋放出來作為current,之後查找這個current的鄰居,如果沒有到訪過而且也不是牆的話,就將其設定成已訪問,並且為它的分數設成current.score + 1,再將這個鄰居放入queue候選列。

最後,在draw()的最上方,加入如果queue候選列長度為零(即已將所有鄰居都找查完,就設成noLoop()return

4. 找出路徑

wavefront2

draw()之中,在之前為每個鄰居都打分數的步驟之上,加入: 如果找到鄰居就是終點要怎樣做。

在打分數時,我們由起點開始,洪水式擴散出去,每走一步就將score加一,直到找到終點。要在終點找到路返回起點,我們只需要順著分數,在4個鄰居中選取累減的數字,就一定是返回原點的路線。

所以我們首先將neighborcurrent加入path中,之後再找出鄰居的鄰居,如果是累減的分數,就將其加入path,再將這個鄰居變成current

考考你: 將效果美化一下

 

wavefront3

試著將程式美化到我這個效果。我是將每個spot的分數scoremap,根據位置去變成不同顏色color(map(spots[i][j].score, 0, cols+rows, 0, 255),記得要先在setup()設定成colorMode(HSN, 255)